import java.util.*;
import java.io.*;

public class Example {
    private static final int BLOCK_SIZE = 8192;

    public static void main(String[] args) {
        System.out.println("此程序用于将同一文件夹下的若干小文件合并为一个大文件。");
        System.out.println("请注意：\n" +
                "1. 只支持合并由此程序的配套程序分割产生的文件，文件的命名应符合“原文件名 - [序号].[后缀名]”的格式。\n" +
                "2. 允许文件夹下包含多个原文件分解而来的分支文件，每一个原文件产生的分支文件都将被合并到一个独立的文件中。\n" +
                "3. 子文件夹下的文件将被忽略。");
        System.out.println("请输入文件夹的路径（支持绝对路径或相对路径）：");
        System.out.print(">");
        Scanner cin = new Scanner(System.in);
        String dir = cin.nextLine();
        File folder = new File(dir);
        if (!folder.exists() || !folder.isDirectory()) {
            System.out.println("目录不存在，请检查您的输入，然后重试。");
            return;
        }
        File[] files = folder.listFiles();
        TreeMap<String, TreeSet<String>> src_branches = new TreeMap<>();
        //解析所有原文件名
        for (File file : files) {
            if (file.isDirectory()) continue;
            String srcFileName = parseSrcFileName(file.getName());
            if (srcFileName == null) continue;
            if (src_branches.containsKey(srcFileName)) {
                src_branches.get(srcFileName).add(file.getName());
            }
            else {
                src_branches.put(srcFileName, new TreeSet<>());
                src_branches.get(srcFileName).add(file.getName());
            }
        }
        int count = 1;
        //将各个原文件的分支分别合并为各个原文件
        for (Map.Entry<String, TreeSet<String>> entry : src_branches.entrySet()) {
            String srcFileName = entry.getKey();
            TreeSet<String> branchNames = entry.getValue();
            System.out.print("正在合并第" + (count++) + "个文件的分支，共发现" + src_branches.size() + "个。");
            int numOfFilesToBeMerged = branchNames.size();
            int numOfFilesMerged = 0;
            int numOfDotsPrinted = 0;
            int numOfDotsToPrint = 0;
            //将一个原文件的分支合并为该原文件
            try (BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream
                    (dir + "/" + srcFileName), BLOCK_SIZE)) {
                for (String branchName : branchNames) {
                    File branchFile = new File(dir + "/" + branchName);
                    mergeFile(branchFile, out);
                    numOfFilesMerged++;
                    if (numOfFilesMerged * 1.0 / numOfFilesToBeMerged > numOfDotsPrinted * 0.1) {
                        numOfDotsToPrint = (int) (numOfFilesMerged * 1.0 / numOfFilesToBeMerged * 10)
                                - numOfDotsPrinted;
                        for (int i = 1; i <= numOfDotsToPrint; i++) System.out.print(".");
                        numOfDotsPrinted += numOfDotsToPrint;
                    }
                }
            }
            catch (IOException ex) {
                System.out.println("在合并分支文件时发生错误，程序被迫中止。");
                return;
            }
            if (numOfDotsPrinted < 10) {
                for (int i = 1; i <= 10 - numOfDotsPrinted; i++) System.out.print(".");
            }
            System.out.println("完成");
        }
    }

    private static void mergeFile(File file, BufferedOutputStream out) throws IOException {
        try (BufferedInputStream in = new BufferedInputStream(new FileInputStream(file), BLOCK_SIZE)) {
            byte[] arr = new byte[BLOCK_SIZE];
            int ret;
            while (true) {
                ret = in.read(arr);
                out.write(arr, 0, ret);
                if (ret < BLOCK_SIZE) return;
            }
        }
    }

    //当传入的文件名不符合约定的格式时，返回null，否则返回原文件名
    private static String parseSrcFileName(String fileName) {
        int dotIndex = fileName.lastIndexOf('.');
        if (dotIndex == -1) dotIndex = fileName.length();
        //检查短横
        int dashIndex = fileName.lastIndexOf('-');
        if (dashIndex <= 1 || dashIndex >= fileName.length() - 2) return null;
        if (!(fileName.substring(dashIndex - 1, dashIndex).equals(" ") &&
                fileName.substring(dashIndex + 1, dashIndex + 2).equals(" ")))
            return null;
        //检查序号
        String id_str = fileName.substring(dashIndex + 2, dotIndex);
        if (!isInt(id_str)) return null;
        //通过所有检查，解析文件名
        return fileName.substring(0, dashIndex - 1) + fileName.substring(dashIndex + 2 + id_str.length());
    }

    private static boolean isInt(String str) {
        if (str == null)
            return false;
        int start = 0;
        if (str.charAt(0) == '-') {
            if (str.length() < 2)
                return false;
            start = 1;
        }
        for (int i = start; i < str.length(); i++) {
            char ch = str.charAt(i);
            if (!(ch >= '0' && ch <= '9'))
                return false;
        }
        return true;
    }
}